/**
 * 
 */
package jnzb;

import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

import nanoxml.XMLParseException;

import jnzb.agent.JNZBNotPresentException;
import jnzb.control.Configuration;
import jnzb.control.remote.JNZBRemoteAgent;
import jnzb.control.util.SimpleFileLogger;
import jnzb.model.binding.NZBBinder;
import jnzb.ui.UserInterface;

/**
 * This is the start point of the application.<br>
 * There are different ways to launch JNZB
 * <ol>
 * <li>Without an instance running with no paramaters</li>
 * <li>Without an instance running with a nzb parameter</li>
 * <li>With an instance running with no parameter and remote agent disabled</li>
 * <li>With an instance running with an nzb parameter and remote agent disabled</li>
 * <li>With an instance running with no parameter and remote agent enabled</li>
 * <li>With an instance running with an nzb parameter and remote agent enabled</li>
 * </ol>
 * In each instance we should do the following
 * <ol>
 * <li>Launch jnzb</li>
 * <li>Launch jnzb and load file</li>
 * <li>Launch a new jnzb</li>
 * <li>Launch a new jnzb and load file</li>
 * <li>Do nothing (or show message)</li>
 * <li>Queue NZB in already running instance</li>
 * </ol>
 * 
 * @author AA1
 * 
 */
public class JNZBLauncher {

	JNZBRemoteAgent jnzbRemote = null;
	private Configuration config;
	private String initialNZB;

	/**
	 * Call the launcher with the first parameter if there was one
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		JNZBLauncher jnzbl = new JNZBLauncher(args.length > 0 ? args[0] : null);
		try {
			jnzbl.preFlightChecks();
			jnzbl.launch();
		} catch (Exception e) {
			// Any startup errors are reported in a window, and the app exits
			UserInterface.setLookAndFeel();
			UserInterface.showError(e.getMessage(), "Error starting JNZB");
		}
	}

	/**
	 * Performs checks for mandatory things before starting the app. Will exit
	 * the app
	 */
	private void preFlightChecks() throws Exception {
		if (!Configuration.configPresent()) {
			String jnzbHome = Configuration.getJNZBHome();
			if (jnzbHome.equals("")) {
				throw new Exception(
						"Configuration file is not present - cannot start.\nHave you defined JNZB_HOME?  See docs.");
			} else {
				throw new Exception(
						"Configuration file is not present - cannot start.\nJNZB_HOME="
								+ jnzbHome);

			}
		}
	}

	/**
	 * Launch JNZB
	 */
	private void launch() {
		// This is the config setting
		boolean remoteEnabled = config.agentEnabled();

		// This is if JNZB is already running
		boolean alreadyRunning = isJNZBAlreadyRunning();

		if (!remoteEnabled) {
			// If remote isnt enabled we just start a new one as we wont be able
			// to determine if its running anyway
			new JNZB(this.initialNZB);
		} else {
			// Remote is enabled
			if (alreadyRunning) {
				// Already running too
				if (this.initialNZB != null && this.initialNZB.length() > 0) {
					// If something was passed in, queue it up remotely
					try {
						// Setup by call to see if its already running - poor
						// code!
						if (isValidNZB()) {
							this.jnzbRemote.queueNZB(this.initialNZB);
						} else {
							UserInterface.setLookAndFeel();
							UserInterface.showError(this.initialNZB
									+ " is not a valid NZB file.",
									"Error queueing NZB");
						}
					} catch (RemoteException e) {
						// Error sending - show some sort of message
						UserInterface.setLookAndFeel();
						UserInterface.showError(
								"Could not queue NZB - try manually",
								"Error queueing NZB");
					}
				} else {
					// Already running but nothing passed in - show some kind of
					// message
					UserInterface.setLookAndFeel();
					UserInterface.showError("No NZB file details given.",
							"Error Queueing NZB");
				}
			} else {
				// Remote enabled but not already running, just start new one
				new JNZB(this.initialNZB);
			}
		}
	}

	/**
	 * Checks that it's a valid file (is it there and is it loadable as an NZB)
	 * 
	 * @return
	 */
	private boolean isValidNZB() {
		try {
			NZBBinder.loadFromFile(this.initialNZB);
		} catch (XMLParseException e) {
			// Not a valid NZB file
			return false;
		} catch (IOException e) {
			// Probably a file not found
			return false;
		}
		return true;
	}

	/**
	 * Initialise a new launcher
	 * 
	 * @param string
	 */
	public JNZBLauncher(String initialNZB) {
		config = Configuration.getInstance();
		this.initialNZB = initialNZB;
	}

	/**
	 * Will look for the remote object in the RMI registry. Only use this if
	 * you've determined that the remote agent is enabled in config.
	 * 
	 * @return
	 */
	private boolean isJNZBAlreadyRunning() {
		try {
			getJNZBRemoteQueue();
		} catch (Exception e) {
			SimpleFileLogger.getInstace().writeRaw(e.getMessage());
			return false;
		}
		return true;
	}

	private JNZBRemoteAgent getJNZBRemoteQueue() throws Exception {
		if (this.jnzbRemote == null) {
			try {
				// Connect to remote JNZB
				this.jnzbRemote = (JNZBRemoteAgent) Naming.lookup(config
						.getRemoteLocation());
			} catch (MalformedURLException e) {
				throw new Exception("Cant queue NZB");
			} catch (ConnectException e) {
				// Start JNZB
				throw new JNZBNotPresentException("No RMI directory", e);
			} catch (RemoteException e) {
				// Dont start JNZB
				throw new Exception("Cant queue NZB");
			} catch (NotBoundException e) {
				// Start JNZB
				throw new JNZBNotPresentException("JNZB not found in RMI", e);
			}
		}
		return this.jnzbRemote;

	}

}
